/*
  telnet_server.cpp -  telnet server functions class

  Copyright (c) 2014 Luc Lebosse. All rights reserved.

  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Lesser General Public
  License as published by the Free Software Foundation; either
  version 2.1 of the License, or (at your option) any later version.

  This library is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  Lesser General Public License for more details.

  You should have received a copy of the GNU Lesser General Public
  License along with this library; if not, write to the Free Software
  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
*/

#ifdef ARDUINO_ARCH_ESP32

#include "config.h"

#if defined (ENABLE_WIFI) &&  defined (ENABLE_TELNET)

#include "wifiservices.h"

#include "grbl.h"

#include "telnet_server.h"
#include "wificonfig.h"
#include <WiFi.h>
#include <Preferences.h>
#include "report.h"
#include "commands.h"


Telnet_Server telnet_server;
bool Telnet_Server::_setupdone = false;
uint16_t Telnet_Server::_port = 0;
WiFiServer * Telnet_Server::_telnetserver = NULL;
WiFiClient Telnet_Server::_telnetClients[MAX_TLNT_CLIENTS];
#ifdef ENABLE_TELNET_WELCOME_MSG
IPAddress Telnet_Server::_telnetClientsIP[MAX_TLNT_CLIENTS];
#endif

Telnet_Server::Telnet_Server(){
    _RXbufferSize = 0;
    _RXbufferpos = 0;
}
Telnet_Server::~Telnet_Server(){
    end();
}


bool Telnet_Server::begin(){
   
    bool no_error = true;
    end();
    Preferences prefs;
    _RXbufferSize = 0;
    _RXbufferpos = 0;;
    prefs.begin(NAMESPACE, true);
    int8_t penabled = prefs.getChar(TELNET_ENABLE_ENTRY, DEFAULT_TELNET_STATE);
    //Get telnet port
    _port = prefs.getUShort(TELNET_PORT_ENTRY, DEFAULT_TELNETSERVER_PORT);
    prefs.end();
    
    if (penabled == 0) return false;
    //create instance
    _telnetserver= new WiFiServer(_port, MAX_TLNT_CLIENTS);
    _telnetserver->setNoDelay(true);
    String s = "[MSG:TELNET Started " + String(_port) + "]\r\n";
    grbl_send(CLIENT_ALL,(char *)s.c_str());
    //start telnet server
    _telnetserver->begin();
    _setupdone = true;
    return no_error;
}

void Telnet_Server::end(){
    _setupdone = false;
    _RXbufferSize = 0;
    _RXbufferpos = 0;
    if (_telnetserver) {
        delete _telnetserver;
        _telnetserver = NULL;
    }
}

void Telnet_Server::clearClients(){
     //check if there are any new clients
    if (_telnetserver->hasClient()){
      uint8_t i;
      for(i = 0; i < MAX_TLNT_CLIENTS; i++){
        //find free/disconnected spot
        if (!_telnetClients[i] || !_telnetClients[i].connected()){
#ifdef ENABLE_TELNET_WELCOME_MSG
          _telnetClientsIP[i] = IPAddress(0, 0, 0, 0);
#endif
          if(_telnetClients[i]) _telnetClients[i].stop();
          _telnetClients[i] = _telnetserver->available();
          break;
        }
      }
      if (i >= MAX_TLNT_CLIENTS) {
        //no free/disconnected spot so reject
        _telnetserver->available().stop();
      }
    }
}

size_t Telnet_Server::write(const uint8_t *buffer, size_t size){
    
    size_t wsize = 0;
    if ( !_setupdone || _telnetserver == NULL) {
        log_d("[TELNET out blocked]");
        return 0;
        }
    clearClients();
    //log_d("[TELNET out]");
    //push UART data to all connected telnet clients
    for(uint8_t i = 0; i < MAX_TLNT_CLIENTS; i++){
        if (_telnetClients[i] && _telnetClients[i].connected()){
            //log_d("[TELNET out connected]");
          wsize = _telnetClients[i].write(buffer, size);
          COMMANDS::wait(0);
        }
    }
    return wsize;
}

void Telnet_Server::handle(){
    COMMANDS::wait(0);
    //check if can read
    if ( !_setupdone || _telnetserver == NULL) {
        return;
        }
    clearClients();
    //check clients for data
    //uint8_t c;
    for(uint8_t i = 0; i < MAX_TLNT_CLIENTS; i++){
      if (_telnetClients[i] && _telnetClients[i].connected()){
#ifdef ENABLE_TELNET_WELCOME_MSG
          if (_telnetClientsIP[i] != _telnetClients[i].remoteIP()){
              report_init_message(CLIENT_TELNET);
              _telnetClientsIP[i] = _telnetClients[i].remoteIP();
            }
#endif
        if(_telnetClients[i].available()){ 
          uint8_t buf[1024];
          COMMANDS::wait(0);
          int readlen = _telnetClients[i].available();
          int writelen = TELNETRXBUFFERSIZE - available();
          if (readlen > 1024) readlen = 1024;
          if (readlen > writelen) readlen = writelen;
          if (readlen > 0) {
              _telnetClients[i].read(buf, readlen);
              push(buf, readlen);
          }	
          return;
        }
      }
      else {
        if (_telnetClients[i]) {
#ifdef ENABLE_TELNET_WELCOME_MSG
          _telnetClientsIP[i] = IPAddress(0, 0, 0, 0);
#endif
          _telnetClients[i].stop();
        }
      }
       COMMANDS::wait(0);
    }
}

int Telnet_Server::peek(void){
    if (_RXbufferSize > 0)return _RXbuffer[_RXbufferpos];
    else return -1;
}

int Telnet_Server::available(){
    return _RXbufferSize;
}

int Telnet_Server::get_rx_buffer_available(){
    return TELNETRXBUFFERSIZE - _RXbufferSize;
}

bool Telnet_Server::push (uint8_t data){
    log_i("[TELNET]push %c",data);
    if ((1 + _RXbufferSize) <= TELNETRXBUFFERSIZE){
        int current = _RXbufferpos + _RXbufferSize;
        if (current > TELNETRXBUFFERSIZE) current = current - TELNETRXBUFFERSIZE;
        if (current > (TELNETRXBUFFERSIZE-1)) current = 0;
        _RXbuffer[current] = data;
        _RXbufferSize++;
        log_i("[TELNET]buffer size %d",_RXbufferSize);
       return true;
    }
    return false;
}

bool Telnet_Server::push (const uint8_t * data, int data_size){
    if ((data_size + _RXbufferSize) <= TELNETRXBUFFERSIZE){
        int data_processed = 0;
        int current = _RXbufferpos + _RXbufferSize;
        if (current > TELNETRXBUFFERSIZE) current = current - TELNETRXBUFFERSIZE;
        for (int i = 0; i < data_size; i++){
        if (current > (TELNETRXBUFFERSIZE-1)) current = 0;
        if (char(data[i]) != '\r') {
            _RXbuffer[current] = data[i];
            current ++;
            data_processed++;
            }
        COMMANDS::wait(0);
        //vTaskDelay(1 / portTICK_RATE_MS);  // Yield to other tasks
        }
        _RXbufferSize+=data_processed;
        return true;
    }
    return false;
}

int Telnet_Server::read(void){
    
    if (_RXbufferSize > 0) {
        int v = _RXbuffer[_RXbufferpos];
        //log_d("[TELNET]read %c",char(v));
        _RXbufferpos++;
        if (_RXbufferpos > (TELNETRXBUFFERSIZE-1))_RXbufferpos = 0;
        _RXbufferSize--;
        return v;
    } else return -1;
}

#endif // Enable TELNET && ENABLE_WIFI

#endif // ARDUINO_ARCH_ESP32
